﻿using System;
using System.Globalization;
using System.Text;

namespace Connection.MRIM
{
    public class Packet
    {
        public byte[] Array;
        private int _len;
        public int GetLen()
        {
            return _len;
        }

        private const int BlockSize = 512;

        public int Magic
        {
            get
            {
                return BitConverter.ToInt32(Array, 0);
            }
            set
            {
                Set(BitConverter.GetBytes(value), 0);
            }
        }
        public int Proto
        {
            get
            {
                return BitConverter.ToInt32(Array, 4);
            }
            set
            {
                Set(BitConverter.GetBytes(value), 4);
            }
        }
        public int Seq
        {
            get
            {
                return BitConverter.ToInt32(Array, 8);
            }
            set
            {
                Set(BitConverter.GetBytes(value), 8);
            }
        }
        public int Message
        {
            get
            {
                return BitConverter.ToInt32(Array, 12);
            }
            set
            {
                Set(BitConverter.GetBytes(value), 12);
            }
        }
        public int Length
        {
            get
            {
                Set(BitConverter.GetBytes(_len - 44), 16);
                return _len;
            }
        }
        public int PacketLen
        {
            get
            {
                return BitConverter.ToInt32(Array, 16);
            }
        }

        public int Ip
        {
            get
            {
                return BitConverter.ToInt32(Array, 20);
            }
            set
            {
                Set(BitConverter.GetBytes(value), 20);
            }
        }
        public int Port
        {
            get
            {
                return BitConverter.ToInt32(Array, 24);
            }
            set
            {
                Set(BitConverter.GetBytes(value), 24);
            }
        }

        private void Resize(int blockCounts = 1)
        {
            System.Array.Resize(ref Array, Array.Length + BlockSize * blockCounts);
        }

        public void AddLpsUtf(string lps)
        {
            byte[] bytes = Encoding.GetEncoding("utf-16").GetBytes(lps);
            AddUl(bytes.Length);
            Add(bytes);
        }
        public void AddLps(string lps)
        {
            byte[] bytes = Encoding.GetEncoding(1251).GetBytes(lps);
            AddUl(bytes.Length);
            Add(bytes);
        }
        public void AddUl(int ul)
        {
            Add(BitConverter.GetBytes(ul));
        }
        public void Add(int ul)
        {
            AddUl(ul);
        }

        public void Add(byte[] bytes, int startIndex, int len)
        {
            int i;
            int empty = Array.Length - _len;
            if (empty < len)
            {
                int bcks = len - empty;
                if (bcks % BlockSize != 0)
                    bcks = bcks / BlockSize + 1;
                else
                    bcks = bcks / BlockSize;
                Resize(bcks);
            }

            for (i = 0; i < len; i++)
            {
                Array[_len++] = bytes[startIndex + i];
            }
        }
        public void Add(byte[] bytes)
        {
            int i;
            int empty = Array.Length - _len;
            if (empty < bytes.Length)
            {
                int bcks = bytes.Length - empty;
                if (bcks % BlockSize != 0)
                    bcks = bcks / BlockSize + 1;
                else
                    bcks = bcks / BlockSize;
                Resize(bcks);
            }

            for (i = 0; i < bytes.Length; i++)
            {
                Array[_len++] = bytes[i];
            }
        }
        public void Add(byte b)
        {
            if (_len == Array.Length)
                Resize();
            Array[_len] = b;
            _len += 1;
        }

        public void Set(byte b, int pos)
        {
            if (pos >= Array.Length)
                System.Array.Resize(ref Array, pos + 1);
            Array[pos] = b;
        }
        public void Set(byte[] bytes, int pos)
        {
            int i;

            if (bytes.Length == 0) return;

            if (pos + bytes.Length > Array.Length)
                System.Array.Resize(ref Array, pos + bytes.Length);

            for (i = 0; i < bytes.Length; i++)
            {
                Array[pos + i] = bytes[i];
            }
        }

        public void Set(byte[] bytes, int startIndex, int len, int pos)
        {
            int i;

            if (bytes.Length == 0 || len == 0) return;

            if (pos + len > Array.Length)
                System.Array.Resize(ref Array, pos + len);

            for (i = 0; i < len; i++)
            {
                Array[pos + i] = bytes[startIndex + i];
            }
        }

        public Packet()
        {
            Array = new byte[BlockSize];
            _len = 0;
        }

        public Packet(byte[] bytes)
        {
            Array = new byte[0];
            _len = 0;
            Add(bytes);
        }

        public Packet(int seq, int message, int ip = 0, int port = 0)
        {
            Array = new byte[Consts.SizeHeader];
            Magic = Consts.CsMagic;
            Proto = Consts.ProtoVersion;
            Seq = seq;
            Message = message;
            Ip = ip;
            Port = port;

            _len = Consts.SizeHeader;
        }

        public object[] Decode(ref int pos, params Data[] tdata)
        {
            int i;
            var data = new object[tdata.Length];

            for (i = 0; i < tdata.Length; i++)
            {
                int len;
                switch (tdata[i])
                {
                    case Data.Ul:
                        data[i] = BitConverter.ToInt32(Array, pos);
                        pos += 4;
                        break;
                    case Data.Lps:
                        len = BitConverter.ToInt32(Array, pos);
                        data[i] = Encoding.GetEncoding(1251).GetString(Array, pos + 4, len);
                        pos += len + 4;
                        break;
                    case Data.LpsUtf:
                        len = BitConverter.ToInt32(Array, pos);
                        data[i] = Encoding.GetEncoding("utf-16").GetString(Array, pos + 4, len);
                        pos += len + 4;
                        break;
                }
            }
            return data;
        }

        public void ShowInFile(string description = "none", string filename = "pack.txt")
        {
            int i;
            string ascii = "";
            System.IO.StreamWriter f = System.IO.File.AppendText(filename);

            f.AutoFlush = true;

            f.Write("Packet [{2}]\nDescription: {0}\nDT: {1}\n", description, DateTime.Now.ToString(CultureInfo.InvariantCulture), _len);

            for (i = 0; i < _len; i++)
            {
                ascii += (Array[i] > 31) && (Array[i] < 127) ? Convert.ToChar(Array[i]).ToString(CultureInfo.InvariantCulture) : ".";
                f.Write(Array[i].ToString("X2"));

                switch (i % 16)
                {
                    case 7: f.Write("-"); break;
                    case 15:
                        f.Write(" : {0} < {1}\n", ascii, ((i - 15).ToString(CultureInfo.InvariantCulture) + "-" + i.ToString(CultureInfo.InvariantCulture)));
                        ascii = "";
                        break;
                    default: f.Write(" "); break;
                }
            }

            if (_len % 16 != 0) f.Write(new string(' ', (16 - _len % 16) * 3) + ": " + ascii);

            f.Write("\n\n");
            f.Close();

        }
    }
}
